#ifndef PARTIDO_H
#define PARTIDO_H
#include <jsoncpp/json/json.h>
#include "Thread.h"
#include <vector>
#include <sstream>
#include <map>
#include <list>
#include "Usuario.h"
#include <time.h>
#include "Mutex.h"
#include "Protocolo.h"
#include "Juego.h"
#include <string>

#define JUGADORES_MINIMOS 2

/** Estados del partido */
#define PARTIDO_INICIANDO 0
#define PARTIDO_EJECUTANDO 1
#define PARTIDO_NO_INICIADO 2
#define PARTIDO_FINALIZADO 4
#define PARTIDO_CANCELADO 5
#define PARTIDO_ELIMINADO 6
#define CANT_JUG_PREDETERMINADA 2
#define NIVEL_INVALIDO -1
#define ID_INVALIDO -1

#define TIMING_PARTIDO 50 // milisegundos


class Partido : public Thread {
  public:
    Partido();
    virtual ~Partido();
    void setId(int id);
    int getId();

    void addJugador(Usuario *jugador);
    void quitarJugador(int uid);

    void setDescripcion(std::string &descripcion);
    int getJugNecesarios();
    void setJugNecesarios(int cant);
    std::string getDescripcion();

    void setNumeroNivel(int nroNivel);
    int getNumeroNivel();

    void *run();

    void getResumenJson(Json::Value &resumen);

    Usuario *getJugadorById(int uid);
    int getCantEsperando() {
        return this->jugadores.size();
    }

    int inicializar();

    void getEstadoJugadores(Json::Value &estado_jugadores);

    bool ejecutando();
    bool terminado();
    bool aceptando();
    bool lleno();

    void iniciar();
    void finalizar();
    void eliminar();
    int mover(std::pair<int,int> origen, std::pair<int,int> destino, 
                            Json::Value &cambios_tablero, int &puntos);

    int procesar(EventoPartido *evento);

    void bloquear();
    void desbloquear();

    void imprimirTablero();
    void getEscenarioInicial(Json::Value &escenario);
    void getCandyAndColor(std::string candystr, std::string &color,
                          std::string &candy);
    void formatearTablero(int numFilas, int numColumnas, Json::Value &tablero,
                          Json::Value &tablero_out);

  private:
    /* Para bloquear todo el partido (acceso concurrente en array del 
       servidor)*/
    Mutex mutex_general;

    std::string descripcion;
    Mutex m_jugadores;
    std::vector<Usuario *> jugadores;
    int estado;
    Mutex m_eventos;
    std::vector<EventoPartido *> eventos;

    Mutex m_puntajes;
    std::map<int, int> puntajes; // uid -> puntos

    int procesarEventos();
    int getPuntosNecesarios();

    Mutex m_tablero;
    Juego juego;

    int id;
    int jugadores_necesarios;
    int nroNivel;

    void procesarEventoMovimiento(EventoPartido *evento);

    int  enviarAJugadores(Mensaje &estado);
    void notificarGanador(Usuario *usuario);
    void notificarEscenarioInicial();
    void notificarFinPartido(std::string causa, EventoPartido 
                                            *evento_disparador);
    void notificarMovimientoInvalido(EventoPartido *evento);

    int sumarPuntos(int uid, int puntos);
    int getPuntos(int uid);


    void eliminarEventos(int tipo_comando);
};

#endif // PARTIDO_H
